# -*- coding: iso-8859-2 -*-

import os
import logging
import traceback
import re
import string
import sys
import datetime
import copy
import random

from time import time

from authorshipanalyzer.message import Message
from authorshipanalyzer.message import MessageReader
from authorshipanalyzer.file import Path
from authorshipanalyzer.textmodifier import MessagePreprocessor
from authorshipanalyzer.message import MessageRepository
from authorshipanalyzer.slot import SlotManager
from authorshipanalyzer.utils import getKey
from authorshipanalyzer.utils import xuniqueCombinations
from authorshipanalyzer.utils import boolConverter
from authorshipanalyzer.authorslot import AuthorSlotManager
from authorshipanalyzer.classifier import Selector
from authorshipanalyzer.classifier import Classifier
from authorshipanalyzer.experiments import SlotSizeExperiment

class AuthorAnalyzer(object):
    
    def __init__(self, argv):
        self.argv = argv
    
    def analyze(self):           
        if len(self.argv) != 2:
            self.usage()
            sys.exit(-1)
            
        properties = self.readProperties()
        
        # Experiment handler
        experiment = SlotSizeExperiment( int(properties["test.size"]) )
        
        # Read slots
        slots = []
        
        authorsToRetrieve = {}
        if properties["authors"]:
            authorsToRetrieve = copy.deepcopy(properties["authors"])
            for author, value in properties["unknown"].items():
                key = getKey(authorsToRetrieve, author)
                if key != None:
                    authorsToRetrieve[key] += value
                else:
                    authorsToRetrieve[author] = value
             
        print "\n"
        print "Retrieving messages and slot creating..."
        start = time()
        if properties["read.repo"] == "true":
            msgRepository = MessageRepository()
            slots = msgRepository.load(authorsToRetrieve)
        else:
            fromItems = properties["read.from"].split("-")
            fromDate = datetime.date(int(fromItems[0]), int(fromItems[1]), 1)
            
            toItems = properties["read.to"].split("-")
            toDate = datetime.date(int(toItems[0]), int(toItems[1]), 3)
            
            mr = MessageReader(Path.getMainDir() + os.sep + "info1")
            messages = mr.readMessages(fromDate, toDate)
            
            sm = SlotManager()
            sm.createSlots(messages, authorsToRetrieve)
            slots = sm.getSlots()
            
            if properties["read.store"] == "true":
                msgRepository = MessageRepository()
                msgRepository.store(slots)
                
        end = time()    
        
        print "\n"
        print "Slots retrieved in %f seconds" % (end - start)
        print "Number of messages: %d" % len(slots)
        
        # Check slots
        retreivedAuthors = {}        
        for slot in slots:
            if retreivedAuthors.has_key(slot.author):
                retreivedAuthors[slot.author] += 1
            else:
                retreivedAuthors[slot.author] = 1
        
        if authorsToRetrieve == {}:
            authorsToRetrieve = properties["unknown"]
            
        print "\n"
        print "Slot table:"
        print "%20s %15s %15s %15s" % ("author", "retrieved", "defined", "sufficient")
        for author in authorsToRetrieve.keys():
            sufficient = "OK"
            if retreivedAuthors[author] - authorsToRetrieve[author] < 0:
                sufficient = "X"
            print "%20s %15s %15s %15s" % (author, retreivedAuthors[author], authorsToRetrieve[author], sufficient)
            
        # select and classify
        for author, counter in properties["unknown"].items():
            
            # counter of experiments for a single author
            expCounter = 0
            
            print "Analyzing %s for %d time..." % (author, expCounter + 1)
            
            authorSlots = []    # slots of an author
            for slot in slots:
                if slot.author == author:
                    authorSlots.append(slot)
                    
            print "Random slots..."
            combinations = []
            for i in range(int(properties['validation.max'])):
                combination = []
                while (len(combination) < counter):
                    rInt = random.randint(0, len(authorSlots) - 1)
                    if rInt not in combination:
                        combination.append(rInt)
                combinations.append(combination)
                                
            slotCombinations = []
            for combination in combinations:
                slotCombination = []
                for cIndex in combination:
                    slotCombination.append(authorSlots[cIndex])
                slotCombinations.append(slotCombination)
                
            print "Start analyzing..."
            for unknownSlots in slotCombinations:
                
                if properties["dump"] == "true":
                    self.storeSlots(unknownSlots)
                
                print "Select slots..."
                knownSlots = copy.deepcopy(slots)
                for kSlot in knownSlots:
                    for uSlot in unknownSlots:
                        if kSlot.text == uSlot.text:
                            knownSlots.remove(kSlot)
                            break
            
                print "Create author slots..."
                knownAsm = AuthorSlotManager.AuthorSlotManager()
                knownAsm.addSlots(knownSlots)
                knownAsm.recompute()
                
                knownAsmTemp = copy.deepcopy(knownAsm)
                                
                for i in range(int(properties["test.size"]), 0, -1):
                
                    if len(unknownSlots) > i:
                        for j in range(i, len(unknownSlots)):
                            del unknownSlots[0]
                        
                    unknownAsm = AuthorSlotManager.AuthorSlotManager()
                    unknownAsm.addSlots(unknownSlots)
                    unknownAsm.recompute()
                
                    print "\n"
                    print "-----------------------------------------------"
                    print "Compare..."
                    
                    print "\n"
                    print "Known slots:"
                    for author, authorSlot in knownAsm.getAuthorSlots().items():
                        print "%s, %d messages" % ( author, len(authorSlot.slots) )
                       
                    print "\n" 
                    print "Unknown slot:"
                    for author, authorSlot in unknownAsm.getAuthorSlots().items():
                        print "%s, %d messages" % ( author, len(authorSlot.slots) )
                        
                    print "\n"
                    print "Selection started..."
                    print "\n"
                
                    # Dismiss these author whose sociolinguistic profile does not suit to the
                    # unknown profile
                    selector = Selector()
                    dismissed = selector.select(unknownAsm.getAuthorSlots().values()[0], \
                                                knownAsm.getAuthorSlots().values())
                    
                    print "\n"
                    print "Selection finished!"
                    
                    for disAuthor in dismissed:
                        knownAsm.removeAuthor(disAuthor)
                    
                    print "%d authors removed!" % len(dismissed)
                    
                    # Classify
                    # features - features used in classification
                    # kernel - linear, gaussian, polynomial or tr
                    print "Classification..."
                    print "---------> %d %d" % (i, len(unknownSlots))
                    classifier = Classifier(properties["features"], properties["kernel"])
                    result = classifier.classify(unknownAsm.getAuthorSlots().values()[0], \
                                                        knownAsm.getAuthorSlots().values())
                    experiment.addResult(len(unknownSlots), unknownAsm.getAuthorSlots().values()[0].author, result)
                    experiment.printResults()
                    
                    # restore slots which were changed because of selection
                    if len(dismissed) > 0:        
                        knownAsm = copy.deepcopy(knownAsmTemp)
                
                # Validation:
                # - cross (each combination will be checked)
                # - single (single experiment)
                if properties["validation"] == "single":
                    break     
                
                # if maximum number of experiments for single author is reached, break
                if int(properties["validation.max"]) == expCounter:
                    break
                
                expCounter += 1
                
    def storeSlots(self, unknownSlots):
        '''
        Store slots in a file
        '''
        
        print "Store selected slots..."
        d = datetime.datetime.now()
        output = "output_%s_%d" % ( d.strftime("%y%m%d_%H.%M.%S"), random.randint(0, sys.maxint) )
        output = os.path.join(Path.getResourceDir(), "dump", output)
        outputFile = file(output, "w")
        for uSlot in unknownSlots:
            outputFile.write("Author: " + uSlot.author + "\n")
            outputFile.write("Subject: " + uSlot.message.getSubject() + "\n")
#            outputFile.write("Date " + str(uSlot.message.getDate()) + "\n\n")
            outputFile.write("\n\n")
            outputFile.write(uSlot.message.getText())
            outputFile.write("\n\n")
            outputFile.write("-----------------------------------------------")
            outputFile.write("\n\n")
        outputFile.flush()
        outputFile.close()
        print "Slots stored in %s" % output    
        

    def usage(self):
        print "Usage: <experiment_file>"
        
    def readProperties(self):
        '''
        Read program property file
        '''
        result = {}
        
        print "\n"
        print "Read properties from %s" % self.argv[1]
        
        features = {}
        
        file = open(os.path.join(Path.getMainDir(), self.argv[1]), "r")
        for line in file.readlines():
            line = line.strip()
            if line != "":
                items = line.split("=")
                assert len(items) == 2
                
                if items[0] == "authors" or items[0] == "unknown":
                    # if no author specified
                    if items[1] == "":
                        result[items[0]] = {}
                    else:
                        authorDict = {}
                        authors = items[1].split(",")
                        for author in authors:
                            aPrms = author.split(":")
                            assert len(aPrms) == 2
                            authorDict[aPrms[0]] = int(aPrms[1])
                        result[items[0]] = authorDict
                elif re.match("features.*", items[0]):
                    features[items[0]] = boolConverter(items[1])
                else:            
                    result[items[0]] = items[1];
        file.close()
        
        result["features"] = features
        return result
    
if __name__ == "__main__":
    AuthorAnalyzer(sys.argv).analyze()
    
